/*****************************************************************************
 *  ENTROPY - emerging network to reduce orwellian potency yield
 *
 *  Copyright (C) 2002 Juergen Buchmueller <pullmoll@stop1984.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software Foundation,
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 *	$Id: base64.c,v 1.5 2005/07/12 23:12:28 pullmoll Exp $
 *****************************************************************************/
#include "base64.h"

static const char b64[] =
	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";

/**
 * @brief Return the value for a base64 digit
 *
 * Convert a base64 character into its numerical equivalent.
 * For now the tilde is converted to 63, too, until some
 * old keys (namely SSK@ keys) are sorted out.
 *
 * @param c character to decode
 *
 * @return numerical value for digit
 */
static int b64_digit(char c)
{
	if (c >= 'A' && c <= 'Z')
		return (uint8_t)(c - 'A');
	if (c >= 'a' && c <= 'z')
		return (uint8_t)(c - 'a' + 26);
	if (c >= '0' && c <= '9')
		return (uint8_t)(c - '0' + 26 + 26);
	if (c == '_')
		return 62;
	if (c == '-')
		return 63;
	if (c == '~')
		return 63;
	return -EINVAL;
}

/**
 * @brief Decode a string of base64 characters to binary
 *
 * Convert a base64 string into its binary equivalent.
 * Initial whitespace in the source string is ignored.
 * The number of characters in the source string determine
 * the number of bytes in the ouput. Each base64 character
 * gives 6 bits, and every 8 bits an output byte is written.
 *
 * @param buff pointer buffer to receive binary data
 * @param size pointer to a size_t initialized to the buffer size
 * @param src base64 string delimited by NUL or non-base64 character
 *
 * @return zero on success
 */
int base64_decode(void *buff, size_t *size, const char *src)
{
	uint8_t *pb = buff;
	int bitcount;
	size_t dst;
	uint32_t bits;

	while (*src && isspace(*src))
		src++;

	for (dst = 0, bits = 0, bitcount = 0; *src; src++) {
		int digit;

		if (0 == *src)
			break;

		digit = b64_digit(*src);
		if (digit < 0)
			break;

		bits |= (digit << bitcount);
		bitcount += 6;
		while (bitcount >= 8) {
			pb[dst++] = (uint8_t)bits;
			bits >>= 8;
			bitcount -= 8;
		}
	}

	if (size) {
		*size = dst;
	}

	return 0;
}

/**
 * @brief Encode a byte array into a string of base64 characters
 *
 * Convert a byte array into its base64 representation.
 * The number of characters in the retruned string is the
 * number of bytes times 8 divided by 6.
 *
 * @param src pointer to byte array
 * @param size size of byte array
 *
 * @return pointer to a the base64 representation of the bytes
 */
const char *base64_encode(const uint8_t *src, size_t size)
{
#undef	BUFCOUNT
#undef	BUFSIZE
#define	BUFCOUNT	16
	static char *buff[BUFCOUNT] = {
		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
	};
	static int which = 0;
	char *dst;
	size_t s, bitcount;
	uint32_t bits;

	which = (which + 1) % BUFCOUNT;
	xfree(buff[which]);
	dst = buff[which] = xcalloc(size * 8 / 6 + 2, sizeof(char));
	for (s = 0, bits = 0, bitcount = 0; s < size; s++) {

		bits |= src[s] << bitcount;
		bitcount += 8;

		while (bitcount >= 6) {
			*dst++ = b64[bits % 64];
			bits >>= 6;
			bitcount -= 6;
		}
	}
	if (bitcount > 0) {
		*dst++ = b64[bits % 64];
	}
	*dst = '\0';

	return buff[which];
}
